package com.xy.job.admin.core

import com.xy.job.admin.domain.NodeRegistry
import com.xy.job.admin.enums.NodeType
import com.xy.job.admin.enums.ScheduleType
import com.xy.job.admin.event.JobInstanceCompleteEvent
import com.xy.job.admin.repository.JobInfoRepository
import com.xy.job.admin.repository.JobInstanceRepository
import com.xy.job.admin.repository.NodeRegistryRepository
import com.xy.job.core.api.request.HeartbeatParam
import com.xy.job.core.api.request.JobFeedbackParam
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime

@Service
class AdminServiceBiz(
    val eventPublisher: ApplicationEventPublisher,
    var jobInfoRepository: JobInfoRepository,
    var jobInstanceRepository: JobInstanceRepository,
    var nodeRegistryRepository: NodeRegistryRepository,
) {

    private val log = LoggerFactory.getLogger(AdminServiceBiz::class.java)

    fun heartbeat(param: HeartbeatParam) {
        val executorAddress = "${param.host}:${param.port}"
        val registry = nodeRegistryRepository.findByNodeNameAndNodeTypeAndNodeAddress(
            nodeName = param.groupCode!!,
            nodeType = NodeType.EXECUTOR,
            nodeAddress = executorAddress
        )
        if (registry != null) {
            registry.lastHeartbeatTime = LocalDateTime.now()
            nodeRegistryRepository.saveAndFlush(registry)
        } else {
            val newRegistry = NodeRegistry().apply {
                this.nodeType = NodeType.EXECUTOR
                this.nodeName = param.groupCode
                this.nodeHost = param.host
                this.nodePort = param.port
                this.nodeAddress = executorAddress
                this.registerTime = LocalDateTime.now()
                this.lastHeartbeatTime = LocalDateTime.now()
            }
            nodeRegistryRepository.saveAndFlush(newRegistry)
        }
    }

    @Transactional
    fun feedback(param: JobFeedbackParam) {
        log.info("receive feedback: jobInstance={}, jobStatus={}", param.jobInstanceId, param.jobStatus)
        val jobInstance = jobInstanceRepository.findById(param.jobInstanceId!!).orElseThrow()
        if (jobInstance.scheduleType == ScheduleType.FIX_DELAY) {
            val jobInfo = jobInfoRepository.findById(jobInstance.jobId!!).orElseThrow()
            jobInfo.nextTriggerTime = LocalDateTime.now().plusSeconds(jobInfo.scheduleConfig!!.toLong())
            jobInfoRepository.save(jobInfo)
        }
        jobInstance.apply {
            this.jobStatus = param.jobStatus
            this.message = param.message
            this.completeTime = LocalDateTime.now()
        }
        jobInstanceRepository.saveAndFlush(jobInstance)
        eventPublisher.publishEvent(JobInstanceCompleteEvent(this, jobInstance))
    }
}